feat(rocm): wvSplitK skinny GEMM for decode M<=4 — the #487 decode GEMM lever - #506
feat(rocm): wvSplitK skinny GEMM for decode M<=4 — the #487 decode GEMM lever#506VikashLoomba wants to merge 1 commit into
Conversation
…routing (mudler#487) Ports vLLM's wvSplitK_hf_sml_ (csrc/rocm/skinny_gemms.cu:351-573) — the split-K, LDS-staged, CU-count-aware skinny GEMM that wins M<=4 shapes — and routes decode-skinny MatmulBT (M 1..4, bf16, K%8==0, activation fits the 64KB LDS stage) to it instead of the 128x128-macro-tile rocBLAS GEMM. gfx1100 (GFX1X/wave32), bf16. VT_ROCM_SKINNY=0 restores the BLAS path for A/B. Measured on 4x RX 7900 XTX (gfx1100), ROCm 7.14, Release: - kernel-level vs the current rocBLAS tile path, same buffers back-to-back: qkv(5120x1024) 2.52x, o_proj(1024x2048) 3.47x, mlp_gateup(3072x1024) 1.78x, lm_head(151936x1024) 3.64x (the issue's worst single case) - in-engine decode, Qwen3-0.6B 128-token steady state: 88.1 vs 70.4 tok/s (+25%) with VT_ROCM_SKINNY on vs off; 0.8B GDN model output unchanged - cross-device: new decode-skinny MatmulBT case green (8/8, NMSE vs CPU) FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: pi:kimi-k3 [pi]
|
Reviewed as part of a sweep over the open external PRs. The port itself is careful — the upstream anchor is cited correctly and I verified Flagging up front: this is the same shape as findings on #523 and #509, so it is worth treating as one lesson rather than three separate reviews. In each case the calculation was ported faithfully and the guards vLLM performs before choosing that calculation were not. 1. Out-of-bounds device write on an odd output dimension. Upstream launches The store at if (threadIdx.x == (kThrds - 1)) {
for (int n = 0; n < N; n++)
for (int y = 0; y < kYtile; y++) C[m + y + n * M] = __float2bfloat16(sum[n][y]);
}
2. No arch guard, and wave32 is hardcoded while the path defaults ON. Upstream guards twice — The port has neither guard and takes only the wave32 arm, while 3. The 4. The upstream test exists and was not ported — and it contains exactly these cases.
Two smaller things while you are in there. The shipped test bounds aggregate NMSE where upstream bounds elementwise ( On the merge conflict: only We have no AMD hardware here, so none of your measurements could be reproduced and I am not disputing them — findings 1–3 are read from the source against the donor at the pin, and are independent of any run. Happy to look again once the guards are back. |
Row
BACKEND-ROCM— M5-adjacent decode perf, the RDNA3 skinny-GEMM path. Issue #487 (decode M=1 GEMMs on 128-tile rocBLAS), coordinating with @joral (gfx1200 generic line). This board is gfx1100 (RDNA3) — the only arch that can test the RDNA3-specific config.What changed
Ports vLLM's
wvSplitK_hf_sml_(csrc/rocm/skinny_gemms.cu, de-torched) into NEWsrc/vt/rocm/rocm_skinny_gemm.hipand routes decode-skinnyMatmulBT(M in 1..4, bf16, K%8==0, activation fits the 64KB LDS stage) to it inrocm_matmul_hipblaslt.hip, ahead of the default-off naive GEMV and the rocBLAS tile path.VT_ROCM_SKINNY=0rolls back to BLAS for A/B. New cross-device case (decode-skinny MatmulBT, bf16, M∈{1,4}) gates it vs the CPU oracle.Evidence (4× RX 7900 XTX gfx1100, ROCm 7.14, Release)
Kernel-level A/B vs the current rocBLAS path (same buffers, back-to-back, many iters):
In-engine decode (Qwen3-0.6B, 128-token steady state): 88.1 vs 70.4 tok/s (+25%) with VT_ROCM_SKINNY on vs off. Qwen3.5-0.8B GDN model output unchanged.
Correctness: ported kernel standalone-validated vs CPU reference across real decode shapes (NMSE ~3e-6, zero bad outputs); new in-tree cross-device case 8/8 green; full ctest zero-delta vs base (same 7 pre-existing host/lane failures).
Speed claims
$GPU_LOCK; recorded here with the repro recipe. (Kernel bench + in-engine A/B, same binary.)Honest gaps